package com.lagou.edu.factory;

import com.alibaba.druid.util.StringUtils;
import com.lagou.edu.anno.MyAutowired;
import com.lagou.edu.anno.MyService;
import com.lagou.edu.anno.MyTransaction;
import com.lagou.edu.utils.ClassUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class AnnotationBeanFactory {

    private static String packageName = "com.lagou.edu";
    private static Map<String,Object> beans = new HashMap<>();

    static {
        try {
            initBeans(getMyServiceClasses());
            attriAssign();
            createTransactionProxy();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //为有@Transaction的类生成代理
    public static void createTransactionProxy() throws Exception {
        ProxyFactory proxyFactory = (ProxyFactory) beans.get("proxyFactory");
        if (proxyFactory == null) {
            throw new Exception("没有找到代理工厂！");
        }
        for (String key : beans.keySet()) {
            Object bean = beans.get(key);
            //判断该类是否有@Transaction注解
            MyTransaction myTransaction = bean.getClass().getDeclaredAnnotation(MyTransaction.class);
            if (myTransaction != null) {
                Class<?>[] interfaces = bean.getClass().getInterfaces();
                if (interfaces != null && interfaces.length > 0) {//如果实现了接口，则使用jdk动态代理
                    beans.put(key,proxyFactory.getJdkProxy(bean));
                } else {//否则使用CGLIB生成代理
                    beans.put(key,proxyFactory.getCglibProxy(bean));
                }
            }
        }
    }

    public static Object getBean(String beanId) throws Exception {
        return beans.get(beanId);
    }

    //使用反射机制获取所有MyService注解的类
    public static List<Class> getMyServiceClasses() throws Exception {
        if (StringUtils.isEmpty(packageName)) {
            throw new Exception("扫包地址不能为空！");
        }
        //获取包下所有类信息，然后循环判断找出有MyService注解的类放入集合中
        List<Class<?>> classesByPackageName = ClassUtils.getClasses(packageName);
        List<Class> myServiceClasses = new ArrayList<>();
        for (Class classinfo : classesByPackageName) {
            MyService myService = (MyService) classinfo.getDeclaredAnnotation(MyService.class);
            if (myService != null) {
                myServiceClasses.add(classinfo);
                continue;
            }
        }
        return myServiceClasses;
    }

    //利用反射初始化所有有MyService注解的Bean,然后放入map中，beanId默认为类名首字母小写，如果MyService有值则beanId使用注解的值
    public static void initBeans(List<Class> myServiceClasses) throws IllegalAccessException, InstantiationException {
        String beanId;
        for (Class classInfo : myServiceClasses) {
            Object newInstance = classInfo.newInstance();
            MyService myService = (MyService)classInfo.getDeclaredAnnotation(MyService.class);
            String valueName = myService.value();
            if(!valueName.equals("")) {
                beanId = myService.value();
            } else {
                beanId = toLowerCaseFirstOne(classInfo.getSimpleName());
            }
            beans.put(beanId,newInstance);
        }
    }

    //找出对象中有MyAutowired注解的字段并注入依赖的bean
    public static void attriAssign() throws Exception {
        for (String key : beans.keySet()) {//遍历容器中bean
            Object bean = beans.get(key);
            Class<? extends Object> classInfo = bean.getClass();
            Field[] declaredFields = classInfo.getDeclaredFields();
            for (Field field : declaredFields) {//遍历字段属性
                MyAutowired myAutowired = (MyAutowired) field.getAnnotation(MyAutowired.class);
                if(myAutowired != null) {//如果该字段有MyAutowired注解
                    String name = field.getName();
                    Class filedClass = field.getType();
                    if(filedClass.isInterface()){//如果该字段是一个接口，需要通过value值去查找
                        name = myAutowired.value();
                    }
                    Object beanref = beans.get(name);
                    if (beanref == null) {
                        throw new Exception("从容器中获取"+name+"失败");
                    }
                    field.setAccessible(true);
                    field.set(bean,beanref);
                    continue;
                }
            }
        }

    }

    //首字母转小写
    public static String toLowerCaseFirstOne(String s) {
        if (Character.isLowerCase(s.charAt(0)))
            return s;
        else
            return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
    }
}
